#include "esp_http_client.h"
#include "esp_tls.h"
#include "esp_crt_bundle.h"
#include "esp_task_wdt.h"

#include "http_request.h"

#define LOG_TAG "http"
static const char *TAG = "HTTP";
#include "esp_log.h"

typedef struct http_req_cb
{
    http_stream_cb_t cb;
    http_header_cb_t header_cb;
    const char *w_data;
    uint16_t w_len;
    void *user_data;
    uint32_t c_length; /* Content-Length */
    char Range[32];    /* Content-Length */
    uint8_t read_header : 1;
} http_req_cb_t;

esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    switch (evt->event_id)
    {
    case HTTP_EVENT_ERROR:
        ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
        break;
    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
        if (evt->user_data)
        {
            http_req_cb_t *cb = (http_req_cb_t *)evt->user_data;
            if (cb->w_data)
            {
                int wlen = esp_http_client_write(evt->client, cb->w_data, cb->w_len);
                if (wlen < 0)
                    ESP_LOGE(TAG, "Write failed");
            }
        }
        break;
    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
        break;
    case HTTP_EVENT_ON_HEADER:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, %s:%s", evt->header_key, evt->header_value);
        if (evt->user_data)
        {
            http_req_cb_t *cb = (http_req_cb_t *)evt->user_data;
            if (cb->header_cb)
                cb->header_cb(cb->user_data, evt->header_key, evt->header_value);
        }

        break;
    case HTTP_EVENT_ON_DATA:
        // size_t size = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
        // ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d free=%d", evt->data_len, size);
        // ESP_LOG_BUFFER_HEX(TAG, evt->data, evt->data_len);

        if (evt->user_data)
        {
            http_req_cb_t *cb = (http_req_cb_t *)evt->user_data;
            if (cb->cb)
            {
                uint8_t tmp = ((char *)evt->data)[evt->data_len];
                ((char *)evt->data)[evt->data_len] = 0;
                cb->cb(cb->user_data, evt->data, evt->data_len);
                ((char *)evt->data)[evt->data_len] = tmp;
            }
        }
        break;
    case HTTP_EVENT_ON_FINISH:
        ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
        break;
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
        int mbedtls_err = 0;
        esp_err_t err = esp_tls_get_and_clear_last_error((esp_tls_error_handle_t)evt->data, &mbedtls_err, NULL);
        if (err != 0)
        {
            ESP_LOGI(TAG, "Last esp error code: 0x%x", err);
            ESP_LOGI(TAG, "Last mbedtls failure: 0x%x", mbedtls_err);
        }
        break;
    case HTTP_EVENT_REDIRECT:
        ESP_LOGI(TAG, "HTTP_EVENT_REDIRECT");
        esp_http_client_set_redirection(evt->client);
        break;
    }
    return ESP_OK;
}

int8_t http_request_from_url(const char *method, char *url, const char *data, uint32_t data_len, http_stream_cb_t cb, void *user_data, http_header_cb_t header_cb)
{
    if (!url || !cb)
        return -1;
    esp_http_client_config_t config = {
        .url = url,
        .event_handler = _http_event_handler,
        .crt_bundle_attach = esp_crt_bundle_attach,
        .skip_cert_common_name_check = true,
        // .buffer_size = 16384,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);

    if (strcmp(method, "POST") == 0)
        esp_http_client_set_method(client, HTTP_METHOD_POST);
    else
        esp_http_client_set_method(client, HTTP_METHOD_GET);
    if (data && data_len)
        esp_http_client_set_header(client, "Content-Type", "application/json");
    http_req_cb_t cb_data = {
        .cb = cb,
        .w_data = data,
        .w_len = data_len,
        .user_data = user_data,
        .header_cb = header_cb,
    };
    esp_http_client_set_user_data(client, &cb_data);
    esp_err_t err = esp_http_client_perform(client);
    if (err == ESP_OK)
    {
        ESP_LOGI(TAG, "HTTP Status = %d, content_length = %" PRId64,
                 esp_http_client_get_status_code(client),
                 esp_http_client_get_content_length(client));
    }
    else
        ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err));
    esp_http_client_cleanup(client);
    size_t size = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
    ESP_LOGI(TAG, "Free %d", size);
    if (cb)
        cb(user_data, NULL, 0);
    return err;
    // return http_request(method, url, server, port, path, data, data_len, cb, user_data, need_header);
}